package com.zhl.config;

import feign.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.function.support.RouterFunctionMapping;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static feign.Util.checkNotNull;

public class FeignLocalInvocationHandler implements InvocationHandler {

    private final Target<?> target;

    private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;

    private final DispatcherServlet dispatcherServlet;

    FeignLocalInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch, DispatcherServlet dispatcherServlet) {
        this.target = checkNotNull(target, "target");
        this.dispatch = checkNotNull(dispatch, "dispatch");
        this.dispatcherServlet = dispatcherServlet;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String a = "12222";

        if (!"equals".equals(method.getName())) {
            if ("hashCode".equals(method.getName())) {
                return this.hashCode();
            } else {
                return "toString".equals(method.getName()) ? this.toString() : this.reflectiveController(proxy, method, args);
            }
        } else {
            try {
                Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return this.equals(otherHandler);
            } catch (IllegalArgumentException var5) {
                return false;
            }
        }
    }

    public Object reflectiveController(Object proxy, Method method, Object[] args){
        // 这里需要根据targeter里面的地址  找到controller  然后反射执行
        System.out.println("我这里是本地调用");
        String url = target.url();
        Object result = null;
        try {
//            org.apache.catalina.connector.Request re = new Request();
//            RequestFacade requestFacade = new RequestFacade();
//            dispatcherServlet.getHandlerMappings()

            String url_ = url.replace("http://test-provider", "");
            Object methodHandler = dispatch.get(method);

            String append = "";
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            if (getMapping != null) {
                String[] pathArr = getMapping.value();
                append = pathArr[0];
            }

            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            if (postMapping != null) {
                String[] pathArr = postMapping.value();
                append = pathArr[0];
            }

            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
            if (requestMapping != null) {
                String[] pathArr = requestMapping.value();
                append = pathArr[0];
            }

            url_ = url_ + append;

            List<HashMap<String, Object>> pairs = getUrlPair(dispatcherServlet);
            for (HashMap<String, Object> map : pairs) {
                if (url_.equals(map.get("url"))) {
                    Method reflectiveMethod = (Method) map.get("method");
                    reflectiveMethod.setAccessible(true);

                    Object bean = map.get("bean");
                    if (bean instanceof String) {
                        bean = dispatcherServlet.getWebApplicationContext().getBean((String)bean);
                    }

                    // 调用方法
                    result = reflectiveMethod.invoke(bean, args);

                    return result;
                }
            }

            return result;
        }catch (Exception e) {
            e.printStackTrace();
        }

        return "hello";
    }

    private List<HashMap<String, Object>> getUrlPair(DispatcherServlet dispatcherServlet) {
        List<HashMap<String, Object>> urlList = new ArrayList<HashMap<String, Object>>();
        List<HandlerMapping> handlerMappings = dispatcherServlet.getHandlerMappings();

        RequestMappingHandlerMapping requestMappingHandlerMapping = null;
        BeanNameUrlHandlerMapping beanNameUrlHandlerMapping = null;
        SimpleUrlHandlerMapping simpleUrlHandlerMapping = null;
        RouterFunctionMapping routerFunctionMapping = null;

        for (HandlerMapping handlerMapping : handlerMappings) {
            System.out.println(handlerMapping.getClass());
            if (handlerMapping instanceof RequestMappingHandlerMapping){
                requestMappingHandlerMapping = (RequestMappingHandlerMapping) handlerMapping;
            }
            if (handlerMapping instanceof BeanNameUrlHandlerMapping) {
                beanNameUrlHandlerMapping = (BeanNameUrlHandlerMapping) handlerMapping;
            }
            if (handlerMapping instanceof SimpleUrlHandlerMapping) {
                simpleUrlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
            }
            if (handlerMapping instanceof RouterFunctionMapping) {
                routerFunctionMapping = (RouterFunctionMapping) handlerMapping;
            }
        }

        Map<?, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();

        for (Map.Entry<?, HandlerMethod> m : map.entrySet()) {
            RequestMappingInfo info = (RequestMappingInfo) m.getKey();
            HandlerMethod method = m.getValue();

            HashMap<String, Object> hashMap = new HashMap<String, Object>();
            PathPatternsRequestCondition p = info.getPathPatternsCondition();
            for (PathPattern pattern : p.getPatterns()) {
                hashMap.put("url", pattern.getPatternString());
            }
            hashMap.put("className", method.getMethod().getDeclaringClass().getName());
            hashMap.put("bean", method.getBean());
            // 类名
            hashMap.put("method", method.getMethod()); // 方法名
            hashMap.put("methodName", method.getMethod().getName()); // 方法名
            RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
            String type = methodsCondition.toString();

            if (type != null && type.startsWith("[") && type.endsWith("]")) {
                type = type.substring(1, type.length() - 1);
                hashMap.put("type", type); // 方法名
            }
            urlList.add(hashMap);
        }

        return urlList;
    }


    @Override
    public boolean equals(Object obj) {
        if (obj instanceof FeignLocalInvocationHandler) {
            FeignLocalInvocationHandler other = (FeignLocalInvocationHandler)obj;
            return this.target.equals(other.target);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return this.target.hashCode();
    }

    @Override
    public String toString() {
        return this.target.toString();
    }
}
